package app

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	"gitee.com/fkil555/gin-extend/app/middlewares"
	"gitee.com/fkil555/gin-extend/client"
	"gitee.com/fkil555/gin-extend/conf"
	"gitee.com/fkil555/gin-extend/gcontext"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

type GEApp struct {
	Gin *gin.Engine
}

func New() (app *GEApp, err error) {
	// 加载配置
	if err = conf.InitConf(conf.MODE_APP); err != nil {
		return
	}

	// 创建APP
	app = &GEApp{}

	// 创建Gin
	app.initGin()

	// 创建客户端
	if err = client.InitClients(); err != nil {
		return
	}

	// 注册中间件
	app.registerMiddleware()

	// 初始化默认路由
	app.initDefaultRoute()

	return
}

// 启动APP
func (app *GEApp) Run() (err error) {
	srv := &http.Server{
		Handler:      app.Gin,
		ReadTimeout:  time.Duration(conf.GEConf.AppConfig.ReadTimeout) * time.Millisecond,
		WriteTimeout: time.Duration(conf.GEConf.AppConfig.WriteTimeout) * time.Millisecond,
	}

	//启动server服务
	go func() {
		if listener, err := net.Listen("tcp", conf.GEConf.AppConfig.Addr); err != nil {
			errMsg := fmt.Sprintf("启动server失败：%+v, %+v ", srv, err)
			panic(errMsg)
		} else {
			srv.Serve(listener)
		}
	}()

	// 等待优雅退出命令
	app.waitGraceExit(srv)

	// 清理关闭一些文件或关闭部分服务
	app.close()

	return
}

// 闭包ge上下文
func (app *GEApp) WithGEContext(geHandle gcontext.GEHandleFunc) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 请求超时控制
		timeoutCtx, cancelFunc := context.WithTimeout(c, time.Duration(conf.GEConf.AppConfig.HandleTimeout)*time.Millisecond)
		defer cancelFunc()

		// GE上下文
		geCtx := gcontext.GEContext{
			Context: timeoutCtx,
			Gin:     c,
		}

		// 回调用户
		geHandle(&geCtx)
	}
}

// 初始化Gin
func (app *GEApp) initGin() {
	if conf.GEConf.Debug != 0 {
		gin.SetMode(gin.DebugMode) // 调试模式
	} else {
		gin.SetMode(gin.ReleaseMode) // 生产模式
	}
	app.Gin = gin.New()
}

// 注册中间件
func (app *GEApp) registerMiddleware() {
	// Metadata
	app.Gin.Use(middlewares.MetadataMiddleware())
	// Recovery崩溃日志(一定要放在最后一位, 否则panic会跳过其下的middleware)
	app.Gin.Use(middlewares.Recovery())
	// Cors
	if conf.GEConf.EnableCors {
		app.Gin.Use(middlewares.Cors())
	}
}

// 初始化默认路由
func (app *GEApp) initDefaultRoute() {
	// 验活接口
	app.Gin.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong\n")
	})

	app.Gin.GET("/health", func(c *gin.Context) {
		c.String(200, `{"message":"ok","addr":"`+conf.GEConf.AppConfig.Addr+`"}`)
	})

	// pprof采样接口
	if conf.GEConf.Pprof != 0 {
		app.registerPprofRoute()
	}

	// prometheus采集接口
	if conf.GEConf.Prometheus != 0 {
		app.registerPrometheusRoute()
	}
}

// 注册采样接口
func (app *GEApp) registerPprofRoute() {
	pprofHandler := func(handler http.HandlerFunc) gin.HandlerFunc {
		return func(c *gin.Context) {
			handler.ServeHTTP(c.Writer, c.Request)
		}
	}
	prefixRouter := app.Gin.Group("/debug/pprof")
	{
		prefixRouter.GET("/", pprofHandler(pprof.Index))
		prefixRouter.GET("/cmdline", pprofHandler(pprof.Cmdline))
		prefixRouter.GET("/profile", pprofHandler(pprof.Profile))
		prefixRouter.POST("/symbol", pprofHandler(pprof.Symbol))
		prefixRouter.GET("/symbol", pprofHandler(pprof.Symbol))
		prefixRouter.GET("/trace", pprofHandler(pprof.Trace))
		prefixRouter.GET("/allocs", pprofHandler(pprof.Handler("allocs").ServeHTTP))
		prefixRouter.GET("/block", pprofHandler(pprof.Handler("block").ServeHTTP))
		prefixRouter.GET("/goroutine", pprofHandler(pprof.Handler("goroutine").ServeHTTP))
		prefixRouter.GET("/heap", pprofHandler(pprof.Handler("heap").ServeHTTP))
		prefixRouter.GET("/mutex", pprofHandler(pprof.Handler("mutex").ServeHTTP))
		prefixRouter.GET("/threadcreate", pprofHandler(pprof.Handler("threadcreate").ServeHTTP))
	}
}

// 注册prometheus接口：https://prometheus.io/docs/guides/go-application/
func (app *GEApp) registerPrometheusRoute() {
	promHandler := promhttp.Handler()
	app.Gin.GET("/metrics", func(c *gin.Context) {
		promHandler.ServeHTTP(c.Writer, c.Request)
	})
}

// 等待优雅退出
func (app *GEApp) waitGraceExit(server *http.Server) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
	for {
		s := <-c
		switch s {
		case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
			fmt.Fprintf(os.Stdout, "%s, 收到信号: %s, 退出倒计时%d毫秒，服务正在退出... \n", time.Now().Format("2006-01-02 15:04:05"), s.String(), conf.GEConf.AppConfig.WaitGraceExit)
			ctx, _ := context.WithTimeout(context.TODO(), time.Duration(conf.GEConf.AppConfig.WaitGraceExit)*time.Millisecond)
			server.Shutdown(ctx)

			return
		case syscall.SIGHUP:
		default:
		}
	}
}

// 清理关闭一些文件或关闭部分服务
func (app *GEApp) close() {
	client.Close()
}

// 初始化运行环境
func init() {
	runtime.GOMAXPROCS(runtime.NumCPU()) // 用满所有核心
}
